1
|
|
|
import React, { Component, createRef } from "react"; |
2
|
|
|
|
3
|
|
|
export interface InfoModalProps { |
4
|
|
|
/** HTML ID for modal attributes */ |
5
|
|
|
id: string; |
6
|
|
|
/** Title that appears within the modal when open */ |
7
|
|
|
title: string; |
8
|
|
|
/** Optional subtitle that appears within the open modal */ |
9
|
|
|
subtitle?: string; |
10
|
|
|
/** Text displayed on the button that displays the modal */ |
11
|
|
|
openText: string; |
12
|
|
|
/** Text displayed on the modal confirmation button */ |
13
|
|
|
confirmText: string; |
14
|
|
|
/** React children */ |
15
|
|
|
children?: React.ReactNode; |
16
|
|
|
} |
17
|
|
|
|
18
|
|
|
interface InfoModalState { |
19
|
|
|
/** Height of the modal itself */ |
20
|
|
|
height: number; |
21
|
|
|
/** Visibility of the modal */ |
22
|
|
|
visible: boolean; |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
class InfoModal extends Component<InfoModalProps, InfoModalState> { |
26
|
|
|
private divElement = createRef<HTMLDivElement>(); |
27
|
|
|
|
28
|
|
|
public constructor(props: InfoModalProps) { |
29
|
|
|
super(props); |
30
|
|
|
|
31
|
|
|
this.state = { height: 0, visible: false }; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
public componentDidMount = (): void => { |
35
|
|
|
const node = this.divElement.current; |
36
|
|
|
if (node) { |
37
|
|
|
const height = node.clientHeight; |
38
|
|
|
this.setState({ height }); |
39
|
|
|
} |
40
|
|
|
}; |
41
|
|
|
|
42
|
|
|
protected handleSizing = (): string => { |
43
|
|
|
const viewportHeight = window.outerHeight; |
44
|
|
|
const { height } = this.state; |
45
|
|
|
|
46
|
|
|
return height > viewportHeight |
47
|
|
|
? "active--overflowing" |
48
|
|
|
: "active--contained"; |
49
|
|
|
}; |
50
|
|
|
|
51
|
|
|
private updateBody = (visible: boolean): void => { |
52
|
|
|
const body = document.querySelector("body"); |
53
|
|
|
if (body) { |
54
|
|
|
body.style.overflow = visible ? "hidden" : "visible"; |
55
|
|
|
} |
56
|
|
|
}; |
57
|
|
|
|
58
|
|
|
public handleOpen = (event: React.MouseEvent<HTMLButtonElement>): void => { |
59
|
|
|
event.preventDefault(); |
60
|
|
|
this.setState(() => ({ visible: true })); |
61
|
|
|
this.updateBody(true); |
62
|
|
|
}; |
63
|
|
|
|
64
|
|
|
public handleClose = (event: React.MouseEvent<HTMLButtonElement>): void => { |
65
|
|
|
event.preventDefault(); |
66
|
|
|
this.setState(() => ({ visible: false })); |
67
|
|
|
this.updateBody(false); |
68
|
|
|
}; |
69
|
|
|
|
70
|
|
|
public render(): React.ReactElement { |
71
|
|
|
const { id, title, subtitle, openText, confirmText, children } = this.props; |
72
|
|
|
|
73
|
|
|
const { visible } = this.state; |
74
|
|
|
|
75
|
|
|
return ( |
76
|
|
|
<> |
77
|
|
|
<div data-c-alignment="center"> |
78
|
|
|
<button |
79
|
|
|
className="modal-info__open-button" |
80
|
|
|
data-c-dialog-id={id} |
81
|
|
|
data-c-dialog-action="open" |
82
|
|
|
type="button" |
83
|
|
|
onClick={this.handleOpen} |
84
|
|
|
> |
85
|
|
|
{openText} |
86
|
|
|
</button> |
87
|
|
|
</div> |
88
|
|
|
|
89
|
|
|
<div data-c-dialog-overlay={visible && "active"} /> |
90
|
|
|
|
91
|
|
|
<div |
92
|
|
|
aria-hidden={visible} |
93
|
|
|
aria-describedby={`${id}-description`} |
94
|
|
|
aria-labelledby={`${id}-title`} |
95
|
|
|
data-c-dialog="" |
96
|
|
|
data-c-dialog-id={id} |
97
|
|
|
data-c-padding="top(double) bottom(double)" |
98
|
|
|
role="dialog" |
99
|
|
|
ref={this.divElement} |
100
|
|
|
> |
101
|
|
|
<div data-c-background="white(100)" data-c-radius="rounded"> |
102
|
|
|
<div |
103
|
|
|
data-c-padding="normal" |
104
|
|
|
data-c-border="bottom(thin, solid, black)" |
105
|
|
|
data-c-background="black(90)" |
106
|
|
|
className="modal-info__header" |
107
|
|
|
> |
108
|
|
|
<h5 |
109
|
|
|
data-c-font-size="h4" |
110
|
|
|
data-c-colour="white" |
111
|
|
|
id={`${id}-title`} |
112
|
|
|
> |
113
|
|
|
{title} |
114
|
|
|
</h5> |
115
|
|
|
|
116
|
|
|
{subtitle && ( |
117
|
|
|
<span data-c-font-size="h5" data-c-margin="top(half)"> |
118
|
|
|
{subtitle} |
119
|
|
|
</span> |
120
|
|
|
)} |
121
|
|
|
|
122
|
|
|
<button |
123
|
|
|
className="modal-info__close-button" |
124
|
|
|
data-c-dialog-action="close" |
125
|
|
|
data-c-dialog-id={id} |
126
|
|
|
type="button" |
127
|
|
|
onClick={this.handleClose} |
128
|
|
|
> |
129
|
|
|
<i className="fas fa-times" /> |
130
|
|
|
</button> |
131
|
|
|
</div> |
132
|
|
|
|
133
|
|
|
<div |
134
|
|
|
data-c-border="bottom(thin, solid, black)" |
135
|
|
|
data-c-padding="normal" |
136
|
|
|
> |
137
|
|
|
<div id={`${id}-description`}>{children}</div> |
138
|
|
|
</div> |
139
|
|
|
|
140
|
|
|
<div data-c-padding="normal"> |
141
|
|
|
<div data-c-alignment="center"> |
142
|
|
|
<button |
143
|
|
|
data-c-button="solid(go)" |
144
|
|
|
data-c-radius="rounded" |
145
|
|
|
data-c-dialog-action="close" |
146
|
|
|
data-c-dialog-id={id} |
147
|
|
|
type="button" |
148
|
|
|
onClick={this.handleClose} |
149
|
|
|
> |
150
|
|
|
{confirmText} |
151
|
|
|
</button> |
152
|
|
|
</div> |
153
|
|
|
</div> |
154
|
|
|
</div> |
155
|
|
|
</div> |
156
|
|
|
</> |
157
|
|
|
); |
158
|
|
|
} |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
export default InfoModal; |
162
|
|
|
|